CLANGD_PATHS = {};
NINJA_PATHS = {};
GDB_PATHS = {};
CMAKE_PATHS = {};

function map_gcc_target(target,path)
	if path:find("\\msys") then
		target = target.."-msys"
	end;
	local gcc_target_map = {
		["x86_64-w64-mingw32"] = "mgw64";
		["i686-w64-mingw32"] = "mgw32";
		["x86_64-w64-mingw32-msys"] = "msys-mgw64";
		["i686-w64-mingw32-msys"] = "msys-mgw32";
	};
	local mapped_target = gcc_target_map[target];
	if mapped_target then
		return mapped_target;
	end;
	return target;
end;

function find_intel_tools(vs_info)
	local tool_types = {
		{
			tool_name_prefix="intel-oneapi_";
			reg_root = [[HKLM\SOFTWARE\WOW6432Node\Intel\1ASuites]];
			bin_x64_postfix = [[bin]];
			bin_x86_postfix = [[bin]];
			cxx_x64_name = "clang-cl.exe";
			cc_x64_name = "clang-cl.exe";
			cxx_x86_name = "clang-cl.exe;-m32";
			cc_x86_name = "clang-cl.exe;-m32";
		},
		{
			tool_name_prefix="intel-oneapi_";
			reg_root = [[HKLM\SOFTWARE\WOW6432Node\Intel\1ASuites]];
			bin_x64_postfix = [[bin]];
			bin_x86_postfix = [[bin]];
			cxx_x64_name = "../bin-llvm/clang-cl.exe";
			cc_x64_name = "../bin-llvm/clang-cl.exe";
			cxx_x86_name = "../bin-llvm/clang-cl.exe;-m32";
			cc_x86_name = "../bin-llvm/clang-cl.exe;-m32";
		},
		{
			tool_name_prefix="intel-icl_";
			reg_root = [[HKLM\SOFTWARE\WOW6432Node\Intel\Suites]];
			bin_x64_postfix = [[bin\intel64]];
			bin_x86_postfix = [[bin\intel64_ia32]];
			cxx_x64_name = "icl.exe";
			cc_x64_name = "icl.exe";
			cxx_x86_name = "icl.exe";
			cc_x86_name = "icl.exe";
		}
	};
	local versions = {}

	local find_intel_tools_impl = function(cfg)
		local intel_toolset_versions = utils:list_reg(cfg.reg_root);
		if intel_toolset_versions == nil or #intel_toolset_versions <=0 then
			return nil;
		end;
		for i, ver in ipairs(intel_toolset_versions) do
			if ver:lower() ~= "defaults" then
				local ver_id = utils:read_reg( make_path(cfg.reg_root, ver,[[Defaults\C++\EM64T\SubKey]]));
				if ver_id then
					local ver_info_reg_root = make_path(cfg.reg_root,ver,ver_id,[[C++]]);
					local toolset_name = utils:read_reg(make_path(ver_info_reg_root, "PlatformToolset"));
					if toolset_name then
						local toolset_dir = utils:read_reg(make_path(ver_info_reg_root, "ProductDir"));
						local toolset_include = utils:read_reg(make_path(ver_info_reg_root, [[EM64T_NATIVE\IncludeDir]]));
						local toolset_lib = utils:read_reg(make_path(ver_info_reg_root, [[EM64T_NATIVE\LibDir]]));
						toolset_include = toolset_include:gsub("%$%(ICInstallDir%)",toolset_dir);
						toolset_lib = toolset_lib:gsub("%$%(ICInstallDir%)",toolset_dir);
						local toolset_bin = make_path(toolset_dir, cfg.bin_x64_postfix);
						local toolset_bin_path = {toolset_bin};
						local toollset_include_path = {split_string(toolset_include,";")};
						local toollset_lib_path = {split_string(toolset_lib,";")};
						local tools_cxx = make_path(toolset_bin, cfg.cxx_x64_name);
						local tools_cc = make_path(toolset_bin, cfg.cc_x64_name);
						if utils:file_time(tools_cxx) ~= nil then
							if vs_info and vs_info.x64tools then
								for i, path in ipairs(vs_info.x64tools.include) do
									table.insert(toollset_include_path, path);
								end;
								for i, path in ipairs(vs_info.x64tools.lib) do
									table.insert(toollset_lib_path, path);
								end;
								for i, path in ipairs(vs_info.x64tools.bin) do
									table.insert(toolset_bin_path, path);
								end;
							end;

							table.insert(versions, {
									name = cfg.tool_name_prefix.."x64-"..ver;
									home = toolset_dir;
									bin = toolset_bin_path;
									include = toollset_include_path;
									lib = toollset_lib_path;
									tools = {
										cxx = tools_cxx;
										cc = tools_cc;
										rc = vs_info.x64tools.tools.rc;
									};
									env = {
										["INTEL_TARGET_ARCH"] = "intel64";
									};
							});
						end;
					end;
				end;
				local ver_id = utils:read_reg(make_path(cfg.reg_root,ver,[[Defaults\C++\IA32\SubKey]]));
				if ver_id then
					local ver_info_reg_root = make_path(cfg.reg_root,ver,ver_id,[[C++]]);
					local toolset_name = utils:read_reg(make_path(ver_info_reg_root, "PlatformToolset"));
					if toolset_name then
						local toolset_dir = utils:read_reg(make_path(ver_info_reg_root, "ProductDir"));
						local toolset_include = utils:read_reg(make_path(ver_info_reg_root, [[IA32\IncludeDir]]));
						local toolset_lib = utils:read_reg(make_path(ver_info_reg_root, [[IA32\LibDir]]));
						toolset_include = toolset_include:gsub("%$%(ICInstallDir%)",toolset_dir);
						toolset_lib = toolset_lib:gsub("%$%(ICInstallDir%)",toolset_dir);
						local toolset_bin = make_path(toolset_dir, cfg.bin_x86_postfix);
						local toolset_bin_path = {toolset_bin};
						local toollset_include_path = {split_string(toolset_include,";")};
						local toollset_lib_path = {split_string(toolset_lib,";")};
						local tools_cxx_x64 = make_path(toolset_bin, cfg.cxx_x64_name);
						local tools_cxx = make_path(toolset_bin, cfg.cxx_x86_name);
						local tools_cc = make_path(toolset_bin, cfg.cc_x86_name);
						if utils:file_time(tools_cxx_x64) ~= nil then
							if vs_info and vs_info.x86tools then
								for i, path in ipairs(vs_info.x86tools.include) do
									table.insert(toollset_include_path, path);
								end;
								for i, path in ipairs(vs_info.x86tools.lib) do
									table.insert(toollset_lib_path, path);
								end;
								for i, path in ipairs(vs_info.x86tools.bin) do
									table.insert(toolset_bin_path, path);
								end;
							end;
							table.insert(versions, {
									name = cfg.tool_name_prefix.."x86-"..ver;
									home = toolset_dir;
									bin = toolset_bin_path;
									include = toollset_include_path;
									lib = toollset_lib_path;
									tools = {
										cxx = tools_cxx;
										cc = tools_cc;
										rc = vs_info.x86tools.tools.rc;
									};
									env = {
										["INTEL_TARGET_ARCH"] = "ia32";
									};
							});
						end;
					end;
				end;
			end;
		end;
	end;

	for i, cfg in ipairs(tool_types) do
		find_intel_tools_impl(cfg);
	end;
	if #versions <= 0 then
		return nil;
	end;
	return versions;
end;

function get_vs_info(ver)
    local vs_info = {}
    local try_get_pair = function(key, val)
        if key then
            return { key; val };
        end
        return nil;
	end;
	if ver == nil then
		ver = 17;
	end;

	local vs_info = try_get_pair(utils:get_program_id(string.format("visualstudio.solution.%d.0", ver)), ver);

	if vs_info == nil then
        return nil
    end

    vs_info.version = vs_info[2];
    local vs_devenv = utils:read_reg(string.format([[HKLM\SOFTWARE\WOW6432Node\Classes\CLSID\%s\LocalServer32\]], vs_info[1]));
    if vs_devenv == nil then
        return nil;
	end

    vs_devenv = string.gsub(vs_devenv, "\"", "");
    vs_info.root = make_path(vs_devenv, [[../../../]])

    local sdk_path = utils:read_reg([[HKLM\SOFTWARE\WOW6432Node\Microsoft\Microsoft SDKs\Windows\v10.0\InstallationFolder]]);
    local sdk_version = utils:read_reg([[HKLM\SOFTWARE\WOW6432Node\Microsoft\Microsoft SDKs\Windows\v10.0\ProductVersion]]);

    if sdk_path ~= nil then
        sdk_version = utils:list_dir(make_path(sdk_path, "Include", sdk_version .. ".*"));
        vs_info.sdk = {
            ["path"] = sdk_path;
            ["version"] = sdk_version;
        };
    end

	if vs_info.version == 16 or vs_info.version == 17 then
        local vc_version = utils:list_dir(make_path(vs_info.root, [[VC\Tools\MSVC]]));
        local paths_x64 = {
            make_path(vs_info.root, [[VC\Tools\MSVC]], vc_version, [[bin\Hostx64\x64]], "cl.exe");
            make_path(vs_info.root, [[VC\Tools\MSVC]], vc_version, [[bin\Hostx86\x64]], "cl.exe");
		};
		
		local paths_x86 = {
			make_path(vs_info.root, [[VC\Tools\MSVC]], vc_version, [[bin\Hostx64\x86]], "cl.exe");
			make_path(vs_info.root, [[VC\Tools\MSVC]], vc_version, [[bin\Hostx86\x86]], "cl.exe");
		};
		local vs_name = "vs"..vs_info.version;
		if vs_info.version == 16 then
			vs_name = "vs2019"
		elseif vs_info.version == 17 then
			vs_name = "vs2022"
		end;
		
        for i, p in ipairs(paths_x64) do
            if utils:file_time(p) ~= nil then
                vs_info.x64tools = {
					["name"] = vs_name.."-x64";
					["version"] = vs_info.version;
                    ["home"] = vs_info.root;
                    ["bin"] = { make_path(p, "..") };
                    ["include"] = { make_path(p, "../../../../include") };
                    ["lib"] = { make_path(p, "../../../../lib/x64") };
                    tools = {
                        ["cc"] = p;
                        ["cxx"] = p;
						["rc"] = make_path(sdk_path, "Bin", sdk_version, "x64","rc.exe");
						["msvc"] = true;
                    }
                };
                break ;
            end
        end
        for i, p in ipairs(paths_x86) do
            if utils:file_time(p) ~= nil then
                vs_info.x86tools = {
					["name"] = vs_name.."-x86";
					["version"] = vs_info.version;
					["home"] = vs_info.root;
                    ["bin"] = { make_path(p, "..") };
                    ["include"] = { make_path(p, "../../../../include") };
                    ["lib"] = { make_path(p, "../../../../lib/x86") };
                    tools = {
                        ["cc"] = p;
						["cxx"] = p;
						["rc"] = make_path(sdk_path, "Bin", sdk_version, "x86","rc.exe");
						["msvc"] = true;
                    }
                };
                break ;
            end
        end
    end
    if sdk_path ~= nil then
        if vs_info.x86tools ~= nil then
            table.insert(vs_info.x86tools.bin, make_path(sdk_path, "Bin", sdk_version, "x86"));
            table.insert(vs_info.x86tools.include, make_path(sdk_path, "Include", sdk_version, "ucrt"));
            table.insert(vs_info.x86tools.include, make_path(sdk_path, "Include", sdk_version, "um"));
            table.insert(vs_info.x86tools.include, make_path(sdk_path, "Include", sdk_version, "shared"));
            table.insert(vs_info.x86tools.include, make_path(sdk_path, "Include", sdk_version, "winrt"));
            table.insert(vs_info.x86tools.lib, make_path(sdk_path, "Lib", sdk_version, "ucrt", "x86"));
            table.insert(vs_info.x86tools.lib, make_path(sdk_path, "Lib", sdk_version, "um", "x86"));
        end
        if vs_info.x64tools ~= nil then
            table.insert(vs_info.x64tools.bin, make_path(sdk_path, "Bin", sdk_version, "x64"));
            table.insert(vs_info.x64tools.include, make_path(sdk_path, "Include", sdk_version, "ucrt"));
            table.insert(vs_info.x64tools.include, make_path(sdk_path, "Include", sdk_version, "um"));
            table.insert(vs_info.x64tools.include, make_path(sdk_path, "Include", sdk_version, "shared"));
            table.insert(vs_info.x64tools.include, make_path(sdk_path, "Include", sdk_version, "winrt"));
            table.insert(vs_info.x64tools.lib, make_path(sdk_path, "Lib", sdk_version, "ucrt", "x64"));
            table.insert(vs_info.x64tools.lib, make_path(sdk_path, "Lib", sdk_version, "um", "x64"));
        end
    end
    return vs_info;
end

function find_windows_terminal()
	local wt_root = find_reg_val([[HKCR\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\Repository\Packages]],"Microsoft.WindowsTerminal", "PackageRootFolder")

	if wt_root == nil then
		return nil;
	end;
	local wt = make_path(wt_root, "wt.exe");
	if utils:file_time(wt) then
		return wt, wt_root, "wt.exe";
	end;
	return;
end;

function find_clang_tools(vs_info)
	local llvm_path = utils:read_reg([[HKLM\SOFTWARE\WOW6432Node\LLVM\LLVM\]]);
	if llvm_path ~= nil then
		local clang_bin = make_path(llvm_path, "Bin");
		local clang_path = make_path(clang_bin, "clang.exe");
		local clang_cl_path = make_path(clang_bin, "clang-cl.exe");
		local clangd_path = make_path(clang_bin, "clangd.exe");
		local clangxx_path = make_path(clang_bin, "clang++.exe");
		local clang_lib_path = make_path(llvm_path, [[lib\clang]]);
		local clang_ver = utils:list_dir(clang_lib_path);

		local x64tools = (vs_info and vs_info.x64tools) or {bin = {}, include = {}, lib = {}, tools={rc=""}};
		local x86tools = (vs_info and vs_info.x86tools) or {bin = {}, include = {}, lib = {}, tools={rc=""}};

		if clang_ver then
			local is_x64 = clang_ver and (utils:list_dir(make_path(clang_lib_path, clang_ver, [[lib\windows\*x86_64.lib]])) ~= nil);
			local has_clang_cl = (utils:file_time(clang_cl_path) ~= nil);

			local clang_tools = {};

			local tools = {
				["clang"] = (utils:file_time(clang_path) ~= nil);
				["clang++"] = (utils:file_time(clangxx_path) ~= nil);
				["lldb"] = (utils:file_time(make_path(clang_bin, "lldb.exe")) ~= nil);
				["clang-format"] = (utils:file_time(make_path(clang_bin, "clang-format.exe")) ~= nil);
				["clang-tidy"] = (utils:file_time(make_path(clang_bin, "clang-tidy.exe")) ~= nil);
			};

			if utils:file_time(clangd_path) then
				tools["clangd"] = clangd_path;
				table.insert(CLANGD_PATHS, clangd_path);
			end;

			if not vs_info then
				return nil;
			end;

			local clang_ver_suffix="-"..clang_ver;
			if is_x64 then
				clang_tools[1] = {
					["name"] = "clang-x64"..clang_ver_suffix;
					home = llvm_path;
					bin = table.clone({clang_bin}, x64tools.bin);
					include = x64tools.include;
					lib = x64tools.lib;
					["tools"] = table.clone(tools,{
						["cc"] = clang_path;
						["cxx"] = clangxx_path;
						rc = x64tools.tools.rc;
					});
				};
				local dummy = has_clang_cl and table.insert(clang_tools, {
						["name"] = "clang-cl-x64"..clang_ver_suffix;
						home = llvm_path;
						bin = table.clone({clang_bin}, x64tools.bin);
						include = x64tools.include;
						lib = x64tools.lib;
						["tools"] = table.clone(tools,{
								["cc"] = clang_cl_path;
								["cxx"] = clang_cl_path;
								rc = x64tools.tools.rc;
								["msvc"] = true;
								["clang"] = true;
						});
				});
				table.insert(clang_tools,{
						["name"] = "clang-x86"..clang_ver_suffix;
						home = llvm_path;
						bin = table.clone({clang_bin}, x86tools.bin);
						include = x86tools.include;
						lib = x86tools.lib;
						["tools"] = table.clone(tools,{
								["cc"] = clang_path..[[;-m32]];
								["cxx"] = clangxx_path..[[;-m32]];
								rc = x86tools.tools.rc;
						});
				});
				local dummy = has_clang_cl and table.insert(clang_tools, {
						["name"] = "clang-cl-x86"..clang_ver_suffix;
						home = llvm_path;
						bin = table.clone({clang_bin}, x86tools.bin);
						include = x86tools.include;
						lib = x86tools.lib;
						["tools"] = table.clone(tools,{
								["cc"] = clang_cl_path..[[;-m32]];
								["cxx"] = clang_cl_path..[[;-m32]];
								rc = x86tools.tools.rc;
								["msvc"] = true;
								["clang"] = true;
						});
				});
			else
				clang_tools[1] = {
					["name"] = "clang-x86"..clang_ver_suffix;
					home = llvm_path;
					bin = table.clone({clang_bin}, x86tools.bin);
					include = x86tools.include;
					lib = x86tools.lib;
					["tools"] = table.clone(tools,{
						["cc"] = clang_path;
						["cxx"] = clangxx_path;
						rc = x86tools.tools.rc;
						["clang"] = true;
					});
				};
				local dummy = has_clang_cl and table.insert(clang_tools, {
						["name"] = "clang-x86"..clang_ver_suffix;
						home = llvm_path;
						bin = table.clone({clang_bin}, x86tools.bin);
						include = x86tools.include;
						lib = x86tools.lib;
						["tools"] = table.clone(tools,{
								["cc"] = clang_cl_path;
								["cxx"] = clang_cl_path;
								rc = x86tools.tools.rc;
								["clang"] = true;
						});
				});
			end;
			return clang_tools;
		end;
	end;
	return nil;
end;

function find_mingw_tools()
	-- [[HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{9bbd9d0c-a70b-4184-8f73-bac63cc23153}]]
	local toolsets = {};
	local mingw_roots = {make_path(edx.app_path, "bin")};
	for i,root in ipairs({utils:list_dir(make_path(edx.app_path, [[\mingw*]]))}) do
		if root then
			table.insert(mingw_roots, make_path(edx.app_path, root, "bin"));
		end;
	end;
	for i,root in ipairs({utils:list_dir(make_path(edx.app_path, [[\toolsets\mingw*]]))}) do
		if root then
			table.insert(mingw_roots, make_path(edx.app_path, "toolsets", root, "bin"));
		end;
	end;
	for i, drive in ipairs({ "C:", "D:", "E:", "F:" }) do
		for i,root in ipairs({utils:list_dir(make_path(drive, [[\mingw*]]))}) do
			if root then
				table.insert(mingw_roots, make_path(drive, root,"bin"));
			end;
		end;
		for i,root in ipairs({utils:list_dir(make_path(drive, [[\msys*]]))}) do
			if root then
				table.insert(mingw_roots, make_path(drive, root,[[mingw32\bin]]));
				table.insert(mingw_roots, make_path(drive, root,[[mingw64\bin]]));
			end;
		end;
	end;
	
	for j, bin_path in ipairs(mingw_roots) do
		local gxx_path = make_path(bin_path, "g++.exe");
		local gcc_path = make_path(bin_path, "gcc.exe");
		local windres_path = make_path(bin_path, "windres.exe");
		local clang_path = make_path(bin_path, "clang.exe");
		local clangd_path = make_path(bin_path, "clangd.exe");
		local clangxx_path = make_path(bin_path, "clang++.exe");
		local ninja_path = make_path(bin_path, "ninja.exe");
		local gdb_path = make_path(bin_path, "gdb.exe");
		local cmake_path = make_path(bin_path, "cmake.exe");
		local clangd_path = make_path(bin_path, "clangd.exe");
		local has_clangd = utils:file_time(clangd_path) ~= nil;
		local has_cmake = utils:file_time(cmake_path) ~= nil;
		local has_gdb = utils:file_time(gdb_path) ~= nil;
		local has_gcc = utils:file_time(gcc_path) ~= nil;
		local has_clang = utils:file_time(clang_path) ~= nil;
		local has_ninja = (utils:file_time(ninja_path) ~= nil);
		if has_ninja then
			table.insert(NINJA_PATHS, ninja_path);
		end;
		if has_gdb then
			table.insert(GDB_PATHS, gdb_path);
		end;
		if has_cmake then
			table.insert(CMAKE_PATHS, cmake_path);
		end;
		if has_clangd then
			table.insert(CLANGD_PATHS, clangd_path);
		end;
		if has_gcc then
			local mingw_tools = {
				bin = { bin_path };
				include = { make_path(bin_path, "../include") };
				lib = { make_path(bin_path, "../lib") };
				tools = {
					["gcc"] = true;
					["g++"] = (utils:file_time(gxx_path) ~= nil);
					["gdb"] = has_gdb and gdb_path;
					["make"] = (utils:file_time(make_path(bin_path, "make.exe")) ~= nil);
					["ninja"] = has_ninja and ninja_path;
					["cc"] = gcc_path;
					["cxx"] = gxx_path;
					["rc"] = windres_path;
				};
			};
			local gcc_type = utils:list_dir(make_path(bin_path, "../lib/gcc/*"));
			local gcc_version = nil;
			local cxx_inc_path = nil;
			if gcc_type ~= nil then
				gcc_version = utils:list_dir(make_path(bin_path, "../lib/gcc", gcc_type));
			end
			if gcc_version ~= nil then
				cxx_inc_path = make_path(bin_path, "../include/c++", gcc_version);
				gcc_pretty_print = make_path(bin_path, "../share/gcc-"..gcc_version, "python");
				if utils:file_time(gcc_pretty_print) == nil then
					gcc_pretty_print = nil;
				end;
				
				table.insert(mingw_tools.include, make_path(bin_path, "..", gcc_type, "include"));
				table.insert(mingw_tools.lib, make_path(bin_path, "..", gcc_type, "lib"));
				table.insert(mingw_tools.include, cxx_inc_path);
				table.insert(mingw_tools.include, make_path(cxx_inc_path, gcc_type));
				local gcc_version_suffix = "-".. gcc_version:gsub("%.0$","");
				local toolset_name = "gcc-".. map_gcc_target(gcc_type,bin_path)..gcc_version_suffix;
				mingw_tools["name"] = toolset_name;
				mingw_tools["home"] = make_path(bin_path, "..");
				mingw_tools.tools["gcc-pretty-print"] = gcc_pretty_print;
				table.insert(toolsets, mingw_tools);

				local has_32_bits = utils:list_dir(make_path(bin_path, "..", gcc_type, [[lib32\crt1.*]])) ~= nil;
				if has_32_bits and (not gcc_path:match("^.*\\msys.*$")) then
					mingw_tools_32 = {
						name = toolset_name .. "-m32";
						home = make_path(bin_path, "..");
						bin = mingw_tools.bin;
						include = mingw_tools.bin;
						lib = {};
						tools = table.clone(mingw_tools.tools,{
								["cc"] = gcc_path..";-m32";
								["cxx"] = gxx_path..";-m32";
								["rc"] = windres_path..";-F pe-i386";
						});
					};
					for i, name in ipairs(mingw_tools.lib) do
						mingw_tools_32.lib[i] = name.."32";
					end;
					table.insert(toolsets, mingw_tools_32);
				end;
			end;
		end;
		if has_clang then
			local mingw_tools = {
				bin = { bin_path };
				include = { make_path(bin_path, "../include") };
				lib = { make_path(bin_path, "../lib") };
				tools = {
					["gcc"] = true;
					["clang"] = true;
					["g++"] = (utils:file_time(gxx_path) ~= nil);
					["gdb"] = has_gdb and gdb_path;
					["make"] = (utils:file_time(make_path(bin_path, "make.exe")) ~= nil);
					["ninja"] = has_ninja and ninja_path;
					["cc"] = clang_path;
					["cxx"] = clangxx_path;
					["rc"] = windres_path;
				};
			};

			if utils:file_time(clangd_path) then
				mingw_tools.tools["clangd"] = clangd_path;
				table.insert(CLANGD_PATHS, clangd_path);
			end;
			
			local gcc_type = utils:list_dir(make_path(bin_path, "../lib/gcc/*"));
			local gcc_version = nil;
			local clang_version = nil;
			local cxx_inc_path = nil;
			if gcc_type ~= nil then
				gcc_version = utils:list_dir(make_path(bin_path, "../lib/gcc", gcc_type));
				clang_version = utils:list_dir(make_path(bin_path, "../lib/clang"));
			end
			if gcc_version ~= nil and clang_version ~= nil then
				cxx_inc_path = make_path(bin_path, "../include/c++", gcc_version);
				gcc_pretty_print = make_path(bin_path, "../share/gcc-"..gcc_version, "python");
				if utils:file_time(gcc_pretty_print) == nil then
					gcc_pretty_print = nil;
				end;
				table.insert(mingw_tools.include, make_path(bin_path, "..", gcc_type, "include"));
				table.insert(mingw_tools.lib, make_path(bin_path, "..", gcc_type, "lib"));
				table.insert(mingw_tools.include, cxx_inc_path);
				table.insert(mingw_tools.include, make_path(bin_path, "../lib/clang", clang_version, "include"));
				table.insert(mingw_tools.include, make_path(cxx_inc_path, gcc_type));

				local gcc_version_suffix = "-".. clang_version;
				local toolset_name = "clang-".. map_gcc_target(gcc_type,bin_path)..gcc_version_suffix;
				mingw_tools["name"] = toolset_name;
				mingw_tools["home"] = make_path(bin_path, "..");
				mingw_tools.tools["gcc-pretty-print"] = gcc_pretty_print;
				table.insert(toolsets, mingw_tools);

				local has_32_bits = utils:list_dir(make_path(bin_path, "..", gcc_type, [[lib32\crt1.*]])) ~= nil;
				if has_32_bits then
					mingw_tools_32 = {
						name = toolset_name .. "-m32";
						home = make_path(bin_path, "..");
						bin = mingw_tools.bin;
						include = mingw_tools.bin;
						lib = {};
						tools = table.clone(mingw_tools.tools,{
								["cc"] = clang_path..";-m32";
								["cxx"] = clangxx_path..";-m32";
								["rc"] = windres_path..";-F pe-i386";
						});
					};
					for i, name in ipairs(mingw_tools.lib) do
						mingw_tools_32.lib[i] = name.."32";
					end;
					table.insert(toolsets, mingw_tools_32);
				end;
			end ;
		end
	end;
	return toolsets;
end;

function find_llvm_mingw_tools()
	local toolsets = {};
	local mingw_roots = {};
	for i, drive in ipairs({ "C:", "D:", "E:", "F:" }) do
		for i,root in ipairs({utils:list_dir(make_path(drive, [[\llvm-mingw*]]))}) do
			if root then
				table.insert(mingw_roots, make_path(drive, root,"bin"));
			end;
		end;
	end;
	for idx, host_type in ipairs({"aarch64", "armv7", "i686", "x86_64"}) do
		for j, bin_path in ipairs(mingw_roots) do
			local type_prefix = host_type..[[-w64-mingw32]];
			local clang_path = make_path(bin_path, type_prefix.."-clang.exe");
			local clangd_path = make_path(bin_path, "clangd.exe");
			local clangxx_path = make_path(bin_path, type_prefix.."-clang++.exe");
			local windres_path = make_path(bin_path, type_prefix.."-windres.exe");
			local ninja_path = make_path(bin_path, "ninja.exe");

			local has_clang = utils:file_time(clang_path);
			if has_clang ~= nil then
				local clang_tools = {
					bin = { bin_path };
					include = { make_path(bin_path, "../include") };
					lib = { make_path(bin_path, "../lib") };
					tools = {
						["gcc"] = true;
						["g++"] = (utils:file_time(make_path(bin_path, "g++.exe")) ~= nil);
						["gdb"] = (utils:file_time(make_path(bin_path, "gdb.exe")) ~= nil);
						["make"] = (utils:file_time(make_path(bin_path, "make.exe")) ~= nil);
						["ninja"] = (utils:file_time(ninja_path) ~= nil) and ninja_path;
						["cc"] = clang_path;
						["cxx"] = clangxx_path;
						["rc"] = windres_path;
					};
				};

				if utils:file_time(clangd_path) then
					clang_tools.tools["clangd"] = clangd_path;
					table.insert(CLANGD_PATHS, clangd_path);
				end;
				
				local cxx_version = utils:list_dir(make_path(bin_path, "../include/c++"));
				local clang_version = utils:list_dir(make_path(bin_path, "../lib/clang"));
				if cxx_version ~= nil and clang_version ~= nil then
					cxx_inc_path = make_path(bin_path, "../include/c++", cxx_version);
					table.insert(clang_tools.lib, make_path(bin_path, "..", type_prefix, "lib"));
					table.insert(clang_tools.include, cxx_inc_path);
					table.insert(clang_tools.include, make_path(bin_path, "../lib/clang", clang_version, "include"));

					local toolset_name = "clang-mgw-"..host_type.."-"..clang_version;
					clang_tools["name"] = toolset_name;
					clang_tools["home"] = make_path(bin_path, "..");
					table.insert(toolsets, clang_tools);
				end ;
			end;
		end;
	end;
	return toolsets;
end;

function find_llvm_msys2_tools()
	local toolsets = {};
	local mingw_roots = {};
	for i, drive in ipairs({ "C:", "D:", "E:", "F:" }) do
		for i,root in ipairs({utils:list_dir(make_path(drive, [[\msys*]]))}) do
			if root then
				table.insert(mingw_roots, make_path(drive, root,[[clang32\bin]]));
				table.insert(mingw_roots, make_path(drive, root,[[clang64\bin]]));
			end;
		end;
	end;
	for idx, host_type in ipairs({"aarch64", "armv7", "i686", "x86_64"}) do
		for j, bin_path in ipairs(mingw_roots) do
			local type_prefix = host_type..[[-w64-mingw32]];
			local clang_path = make_path(bin_path, type_prefix.."-clang.exe");
			local clangd_path = make_path(bin_path, "clangd.exe");
			local clangxx_path = make_path(bin_path, type_prefix.."-clang++.exe");
			local windres_path = make_path(bin_path, "windres.exe");

			local has_clang = utils:file_time(clang_path);
			if has_clang ~= nil then
				local clang_tools = {
					bin = { bin_path };
					include = { make_path(bin_path, "../include") };
					lib = { make_path(bin_path, "../lib") };
					tools = {
						["gcc"] = true;
						["clang"] = true;
						["g++"] = (utils:file_time(make_path(bin_path, "g++.exe")) ~= nil);
						["gdb"] = (utils:file_time(make_path(bin_path, "gdb.exe")) ~= nil);
						["make"] = (utils:file_time(make_path(bin_path, "make.exe")) ~= nil);
						["ninja"] = (utils:file_time(make_path(bin_path, "ninja.exe")) ~= nil);
						["cc"] = clang_path;
						["cxx"] = clangxx_path;
						["rc"] = windres_path;
					};
				};

				if utils:file_time(clangd_path) then
					clang_tools.tools["clangd"] = clangd_path;
					table.insert(CLANGD_PATHS, clangd_path);
				end;

				local cxx_version = utils:list_dir(make_path(bin_path, "../include/c++"));
				local clang_version = utils:list_dir(make_path(bin_path, "../lib/clang"));
				if cxx_version ~= nil and clang_version ~= nil then
					cxx_inc_path = make_path(bin_path, "../include/c++", cxx_version);
					table.insert(clang_tools.lib, make_path(bin_path, "..", type_prefix, "lib"));
					table.insert(clang_tools.include, cxx_inc_path);
					table.insert(clang_tools.include, make_path(bin_path, "../lib/clang", clang_version, "include"));

					local toolset_name = "clang-msys-"..host_type.."-"..clang_version;
					clang_tools["name"] = toolset_name;
					clang_tools["home"] = make_path(bin_path, "..");
					table.insert(toolsets, clang_tools);
				end ;
			end;
		end;
	end;
	return toolsets;
end;

function find_jlink_tools()
	local jlink_install_path = utils:read_reg([[HKLM\SOFTWARE\SEGGER\J-Link\InstallPath]]);
	if jlink_install_path == nil then
		return;
	end;

	local ver_val = utils:read_reg([[HKLM\SOFTWARE\SEGGER\J-Link\CurrentVersion]]);
	local sub_ver = ver_val%10000;
	local major_ver = math.floor(ver_val/10000);
	local patch_ver = sub_ver%100;
	sub_ver = math.floor(sub_ver/100);
	local patch_ver = ("abcdefghijklmnopqrstuvwxyz"):sub(patch_ver, patch_ver);
	local ver_str = string.format("%d.%d%s", major_ver, sub_ver, patch_ver);
	local jlink_gdb_server_path = make_path(jlink_install_path, "JLinkGDBServerCL.exe");
	print(jlink_install_path, "\n", ver_str, "\n", jlink_gdb_server_path, "\n");
end;

function find_wsl_tools()
	local wsl_path = make_path(os.getenv("windir"), "system32/wsl.exe");
	if not utils:file_time(wsl_path) then
		return {};
	end;

	local wsl_reg_root = [[HKCU\Software\Microsoft\Windows\CurrentVersion\Lxss]]
	local dist_keys = utils:list_reg(wsl_reg_root);
	if dist_keys == nil then
		return {};
	end;
	local toolsets = {};
	local wsl_default_dist = utils:read_reg(wsl_reg_root.."\\DefaultDistribution");
	local wsl_dist = {};
	for i, key in ipairs(dist_keys) do
		wsl_dist[i] = utils:read_reg(wsl_reg_root.."\\"..key.."\\DistributionName");
		base_path = utils:read_reg(wsl_reg_root.."\\"..key.."\\BasePath");
		if wsl_default_dist == key then
			wsl_default_dist = wsl_dist[i];
		end;
		local dist_name = wsl_dist[i];
		local wsl_root_path = string.format([[\\wsl$\%s\]], dist_name);
		local wsl_toolset = {
			["name"] = "WSL-"..dist_name;
			["home"] = base_path:sub(5);
			wsl = dist_name;
			encoding = "utf-8";
			suffixes = {
				exec = "";
				shared = ".so";
				lib = ".a";
			},
			bin = {},
			include = {
				"\\"..make_path(wsl_root_path, "include");
				"\\"..make_path(wsl_root_path, "/usr/include");
				"\\"..make_path(wsl_root_path, "/usr/local/include");
			},
			lib = {},
			tools = {
				["gcc"] = true;
				["g++"] = "g++";
				["gdb"] = "make";
				["ninja"] = "ninja";
				["cc"] = "gcc";
				["cxx"] = "g++";
				["gdb"] = "gdb";
				["rc"] = "";
				["cmake"] = "cmake";
				["gcc-pretty-print"] = "/usr/share/gcc/python";
			};
			wrap_command = function(cmd)
				local prefix = "wsl -d "..dist_name.." -- bash -login -c ";
				local altered_cmd = cmd:gsub("\"", "\\\"");
				return prefix .. "\"" .. altered_cmd .. "\"";
			end;
			map_path = function(path)
				path = path:gsub([[^%\%\wsl%$%\]]..dist_name, "/");
				path = path:gsub("\\", "/");
				return path:gsub("^\"?(%w)(:)", function(a)
						return "/mnt/"..(a):lower();
				end);
			end;
			unmap_path = function(path)
				path = path:gsub("/[^/]+/%.%.[/$]", "/");
				if path:sub(1,4) == "/mnt" then
					path = path:gsub("^/mnt/(%w)/", function(a)
							return a:upper()..":\\";
					end);
				elseif path:sub(1,1) == "/" then
					path = path:gsub("^/", function()
							return wsl_root_path;
					end);
				end;
				return path:gsub("/", "\\");
			end;
			get_pretty_print_path = function(self)
				local gcc_shared_paths = utils:list_dir(self.unmap_path("/usr/local/share/gcc-*"));
				if gcc_shared_paths then
					local gcc_version = gcc_shared_paths:sub(5);
					return "/usr/local/share/gcc-"..gcc_version.."/python";
				end;

				gcc_shared_paths = utils:list_dir(self.unmap_path("/usr/share/gcc-*"));
				if gcc_shared_paths then
					local gcc_version = gcc_shared_paths:sub(5);
					return "/usr/share/gcc-"..gcc_version.."/python";
				end;
				return self.tools["gcc-pretty-print"];
			end;
		};
		table.insert(toolsets, wsl_toolset);
	end;
	return toolsets;
end;

function search_compilers(callback)
	local toolsets = {};
	local vs_info = get_vs_info(17);
	local vs_info_2019 = get_vs_info(16);
	if vs_info == nil then
		vs_info = vs_info_2019;
		vs_info_2019 = nil;
	end;

	if vs_info then
		if vs_info.x64tools ~= nil then
			table.insert(toolsets, vs_info.x64tools);
		end ;

		if vs_info.x86tools ~= nil then
			table.insert(toolsets, vs_info.x86tools);
		end;

		if vs_info_2019 then
			if vs_info_2019.x64tools ~= nil then
				table.insert(toolsets, vs_info_2019.x64tools);
			end ;

			if vs_info.x86tools ~= nil then
				table.insert(toolsets, vs_info_2019.x86tools);
			end;
		end;
		
		local clang_tools = find_clang_tools(vs_info);
		if clang_tools ~= nil then
			for i,tools in ipairs(clang_tools) do
				table.insert(toolsets, tools);
			end
		end;

		local intel_tools = find_intel_tools(vs_info);
		if intel_tools then
			for i, info in ipairs(intel_tools) do
				table.insert(toolsets,info);
			end;
		end;
	end;
	


	local mingw_tools = find_mingw_tools();
	if mingw_tools then
		for i, info in ipairs(mingw_tools) do
			table.insert(toolsets,info);
		end;
	end;
	
	local llvm_msys2_tools = find_llvm_msys2_tools();
	if llvm_msys2_tools then
		for i, info in ipairs(llvm_msys2_tools) do
			table.insert(toolsets,info);
		end;
	end;

	local llvm_mingw_tools = find_llvm_mingw_tools();
	if llvm_mingw_tools then
		for i, info in ipairs(llvm_mingw_tools) do
			table.insert(toolsets,info);
		end;
	end;

	local wsl_tools = find_wsl_tools();
	if wsl_tools then
		for i, info in ipairs(wsl_tools) do
			table.insert(toolsets,info);
		end;
	end;

	if NINJA_PATHS[1] then
		NINJA_PATH = NINJA_PATHS[1];
	else
		NINJA_PATH = [[ninja]];
	end;

	if CLANGD_PATHS[1] then
		CLANGD_PATH = CLANGD_PATHS[1];
	else
		CLANGD_PATH = [[clangd]];
	end;

	if callback then
		callback(toolsets);
	end;
    return toolsets;
end;
